package com.test.redislock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Author zz-lxf
 * @E-mail 629938044@qq.com
 * @Date 2021/12/13 15:28
 * @Version 1.0
 */
@Component
public class RedisLock {

    Logger log = LoggerFactory.getLogger(RedisLock.class);

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 添加元素
     *
     * @param key
     * @param value
     */
    public void set(String key, String value) {
        if (key == null || value == null) {
            return;
        }
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 如果已经存在返回false，否则返回true
     *
     * @param key
     * @param value
     * @param expireTime
     * @param mimeUnit
     * @return
     */
    public Boolean setNx(String key, String value, Long expireTime, TimeUnit mimeUnit) {
        if (key == null || value == null) {
            return false;
        }
        // 在spring boot 2 可以直接使用 redisTemplate的setIfAbsent设置key-value和过期时间，是原子性
        Boolean tf = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, mimeUnit);
        return tf;
    }


    /**
     * 获取数据
     *
     * @param key
     * @return
     */
    public Object get(String key) {
        if (key == null) {
            return null;
        }
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 删除
     *
     * @param key
     */
    public void remove(Object key) {
        if (key == null) {
            return;
        }
        redisTemplate.delete(key);
    }

    /**
     * 加锁
     *
     * @param key
     * @param waitTime   等待时间,在这个时间内会多次尝试获取锁，超过这个时间还没获得锁，就返回false
     * @param interval   间隔时间,每隔多长时间尝试一次获的锁
     * @param expireTime key的过期时间
     * @return
     */
    public Boolean lock(String key, Long waitTime, Long interval, Long expireTime) {
        String value = UUID.randomUUID().toString().replaceAll("-", "").toLowerCase();
        Boolean flag = setNx(key, value, expireTime, TimeUnit.MILLISECONDS);
        // 尝试获取锁 成功返回
        if (flag) {
            return flag;
        } else {
            // 获取失败
            // 现在时间
            long nowTime = System.currentTimeMillis();
            // 等待过期时间
            long loseTime = nowTime + waitTime;
            // 不断尝试获取锁成功返回
            while (System.currentTimeMillis() < loseTime) {
                Boolean testFlag = setNx(key, value, expireTime, TimeUnit.MILLISECONDS);
                if (testFlag) {
                    return testFlag;
                }
                try {
                    Thread.sleep(interval);
                } catch (InterruptedException e) {
                    log.error("获取锁异常", e);
                }
            }
        }
        return false;
    }

    /**
     * 释放锁
     *
     * @param key
     */
    public void unLock(String key) {
        remove(key);
    }

    public Boolean setIfAbsent(String key, String value) {
        Boolean tf = redisTemplate.opsForValue().setIfAbsent(key, value);
        redisTemplate.expire(key, 60, TimeUnit.DAYS);
        return tf;
    }
}
